#include "stdafx.h"

#include "Runlist.h"
#include "Attrs.h"

#include <Windows.h>
#include "CAttribute.h"

static INT64 ExtractInt64( char* dptr, int sz ) 
{
	INT64 ret = 0;
	for (int i = sz-1;i>=0;i--) {
		ret = (ret<<8)| (BYTE)(*(dptr+i));
	}
	//sign extend
	if (ret & (1<<(8*sz-1))) {
		for (int i = 63;i>=sz*8;i--)
			ret |= ((UINT64)1)<<i;
	}
	return ret;
}

RunList::RunList(NonResidentAttrHdr * owner,UINT32 clusterSize)
:_clusterSize(clusterSize)
{
	char * dptr = (char*)(owner)+owner->RunlistOffset;
	int totalVCN = 0;
	UINT64 baseRunOffset = 0;
	while (totalVCN<owner->EndVCN-owner->StartVCN+1) {
		int szRunLen = *dptr & 0xf;//low 
		int szRunOffset = *dptr>>4;//high
		INT64 runLen = ExtractInt64(dptr+1,szRunLen);
		INT64 runOffset = ExtractInt64(dptr+1+szRunLen,szRunOffset);
		//increment
		dptr += 1 + szRunLen+szRunOffset;
		totalVCN += runLen;
		_runs.push_back(Run(runOffset,runLen));
	}
	return;
}


RunList::~RunList(void)
{
}

UINT64 RunList::getPosOnDisk( UINT64 startByte )
{
	UINT64 cnt = 0;
	UINT64 precnt = 0;
	UINT64 startCluster = 0;
	int i = 0;
	do {
		precnt = cnt;
		cnt+=_runs[i].Len * _clusterSize;
		startCluster += _runs[i].Offset;
		i++;
	} while (cnt<=startByte);
	return startCluster * _clusterSize+(startByte-precnt);
}

UINT64 RunList::PhyToLogic( UINT64 phyOffset )
{
	UINT64 cnt = 0;
	UINT64 lBnd = _runs[0].Offset * _clusterSize;
	UINT64 rBnd = lBnd + _runs[0].Len * _clusterSize;
	int i = 1;
	while (!(lBnd<=phyOffset && phyOffset<rBnd) && i<_runs.size()) {
		lBnd += _runs[i].Offset * _clusterSize;
		rBnd = lBnd + _runs[i].Len * _clusterSize;
		cnt+=_runs[i-1].Len * _clusterSize;
		i++;
	}
	if (lBnd<=phyOffset && phyOffset<rBnd) //< valid 
	{
		return cnt+phyOffset-lBnd;
	} else 
		return -1;
}

QList<Range> RunList::getClusterRange()
{
	INT64 start=0;
	QList<Range> ret;
	for (int i = 0;i<_runs.size();i++) {
		start += _runs[i].Offset;
		ret.push_back(Range(start,start+_runs[i].Len-1));
	}
	return ret;

}

//TODO: test if this always works. should really do sth instead of simple return
void RunList::Combine( RunList other )
{
	return ;
	if (_runs[0].Offset < other._runs[0].Offset) {
		other._runs[0].Offset-=_getTailOffset();
		_runs.append(other._runs);
		//_runs.insert(_runs.end(),other._runs.begin(),other._runs.end());
	} else if (_runs[0].Offset == other._runs[0].Offset) {
		return;//several attribute reside on  same cluster
	} else 
		Q_ASSERT(0);// TODO :
}

UINT64 RunList::_getTailOffset()
{
	UINT64 ret = 0;
	for (int i = 0;i<_runs.size();i++)
		ret+=_runs[i].Offset;
	return ret;
}
